{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "2NnuRIZedJmK"
   },
   "source": [
    "# Content Based Filtering by hand\n",
    "\n",
    "\n",
    "**Learning Objectives**\n",
    "\n",
    "1. Create and compute a user feature matrix.\n",
    "2. Compute where each user lies in the feature embedding space.\n",
    "3. Create recommendations for new movies based on similarity measures between the user and movie feature vectors.\n",
    "\n",
    "## Introduction\n",
    "\n",
    "This notebook illustrates how to implement a content based filter using low level Tensorflow operations.  \n",
    "The code here follows the technique explained in Module 2 of Recommendation Engines: Content Based Filtering.\n",
    "\n",
    "Each learning objective will correspond to a __#TODO__ in the [student lab notebook](../labs/content_based_by_hand.ipynb) -- try to complete that notebook first before reviewing this solution notebook. \n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To run this lab, we need to use TensorFlow version 2.X. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Requirement already satisfied: tensorflow in /opt/conda/lib/python3.7/site-packages (2.6.3)\n",
      "Requirement already satisfied: wrapt~=1.12.1 in /opt/conda/lib/python3.7/site-packages (from tensorflow) (1.12.1)\n",
      "Requirement already satisfied: keras<2.7,>=2.6.0 in /opt/conda/lib/python3.7/site-packages (from tensorflow) (2.6.0)\n",
      "Requirement already satisfied: gast==0.4.0 in /opt/conda/lib/python3.7/site-packages (from tensorflow) (0.4.0)\n",
      "Requirement already satisfied: flatbuffers~=1.12.0 in /opt/conda/lib/python3.7/site-packages (from tensorflow) (1.12)\n",
      "Requirement already satisfied: wheel~=0.35 in /opt/conda/lib/python3.7/site-packages (from tensorflow) (0.37.1)\n",
      "Requirement already satisfied: opt-einsum~=3.3.0 in /opt/conda/lib/python3.7/site-packages (from tensorflow) (3.3.0)\n",
      "Requirement already satisfied: clang~=5.0 in /opt/conda/lib/python3.7/site-packages (from tensorflow) (5.0)\n",
      "Requirement already satisfied: typing-extensions<3.11,>=3.7 in /opt/conda/lib/python3.7/site-packages (from tensorflow) (3.10.0.2)\n",
      "Requirement already satisfied: protobuf>=3.9.2 in /opt/conda/lib/python3.7/site-packages (from tensorflow) (3.19.4)\n",
      "Requirement already satisfied: keras-preprocessing~=1.1.2 in /opt/conda/lib/python3.7/site-packages (from tensorflow) (1.1.2)\n",
      "Requirement already satisfied: grpcio<2.0,>=1.37.0 in /opt/conda/lib/python3.7/site-packages (from tensorflow) (1.44.0)\n",
      "Requirement already satisfied: tensorflow-estimator<2.7,>=2.6.0 in /opt/conda/lib/python3.7/site-packages (from tensorflow) (2.6.0)\n",
      "Requirement already satisfied: astunparse~=1.6.3 in /opt/conda/lib/python3.7/site-packages (from tensorflow) (1.6.3)\n",
      "Requirement already satisfied: six~=1.15.0 in /opt/conda/lib/python3.7/site-packages (from tensorflow) (1.15.0)\n",
      "Requirement already satisfied: termcolor~=1.1.0 in /opt/conda/lib/python3.7/site-packages (from tensorflow) (1.1.0)\n",
      "Requirement already satisfied: google-pasta~=0.2 in /opt/conda/lib/python3.7/site-packages (from tensorflow) (0.2.0)\n",
      "Requirement already satisfied: numpy~=1.19.2 in /opt/conda/lib/python3.7/site-packages (from tensorflow) (1.19.5)\n",
      "Requirement already satisfied: h5py~=3.1.0 in /opt/conda/lib/python3.7/site-packages (from tensorflow) (3.1.0)\n",
      "Requirement already satisfied: absl-py~=0.10 in /opt/conda/lib/python3.7/site-packages (from tensorflow) (0.15.0)\n",
      "Requirement already satisfied: tensorboard<2.7,>=2.6.0 in /opt/conda/lib/python3.7/site-packages (from tensorflow) (2.6.0)\n",
      "Requirement already satisfied: cached-property in /opt/conda/lib/python3.7/site-packages (from h5py~=3.1.0->tensorflow) (1.5.2)\n",
      "Requirement already satisfied: werkzeug>=0.11.15 in /opt/conda/lib/python3.7/site-packages (from tensorboard<2.7,>=2.6.0->tensorflow) (2.0.3)\n",
      "Requirement already satisfied: requests<3,>=2.21.0 in /opt/conda/lib/python3.7/site-packages (from tensorboard<2.7,>=2.6.0->tensorflow) (2.27.1)\n",
      "Requirement already satisfied: setuptools>=41.0.0 in /opt/conda/lib/python3.7/site-packages (from tensorboard<2.7,>=2.6.0->tensorflow) (59.8.0)\n",
      "Requirement already satisfied: google-auth<2,>=1.6.3 in /opt/conda/lib/python3.7/site-packages (from tensorboard<2.7,>=2.6.0->tensorflow) (1.35.0)\n",
      "Requirement already satisfied: markdown>=2.6.8 in /opt/conda/lib/python3.7/site-packages (from tensorboard<2.7,>=2.6.0->tensorflow) (3.3.6)\n",
      "Requirement already satisfied: tensorboard-data-server<0.7.0,>=0.6.0 in /opt/conda/lib/python3.7/site-packages (from tensorboard<2.7,>=2.6.0->tensorflow) (0.6.1)\n",
      "Requirement already satisfied: google-auth-oauthlib<0.5,>=0.4.1 in /opt/conda/lib/python3.7/site-packages (from tensorboard<2.7,>=2.6.0->tensorflow) (0.4.6)\n",
      "Requirement already satisfied: tensorboard-plugin-wit>=1.6.0 in /opt/conda/lib/python3.7/site-packages (from tensorboard<2.7,>=2.6.0->tensorflow) (1.8.1)\n",
      "Requirement already satisfied: rsa<5,>=3.1.4 in /opt/conda/lib/python3.7/site-packages (from google-auth<2,>=1.6.3->tensorboard<2.7,>=2.6.0->tensorflow) (4.8)\n",
      "Requirement already satisfied: cachetools<5.0,>=2.0.0 in /opt/conda/lib/python3.7/site-packages (from google-auth<2,>=1.6.3->tensorboard<2.7,>=2.6.0->tensorflow) (4.2.4)\n",
      "Requirement already satisfied: pyasn1-modules>=0.2.1 in /opt/conda/lib/python3.7/site-packages (from google-auth<2,>=1.6.3->tensorboard<2.7,>=2.6.0->tensorflow) (0.2.7)\n",
      "Requirement already satisfied: requests-oauthlib>=0.7.0 in /opt/conda/lib/python3.7/site-packages (from google-auth-oauthlib<0.5,>=0.4.1->tensorboard<2.7,>=2.6.0->tensorflow) (1.3.1)\n",
      "Requirement already satisfied: importlib-metadata>=4.4 in /opt/conda/lib/python3.7/site-packages (from markdown>=2.6.8->tensorboard<2.7,>=2.6.0->tensorflow) (4.11.3)\n",
      "Requirement already satisfied: urllib3<1.27,>=1.21.1 in /opt/conda/lib/python3.7/site-packages (from requests<3,>=2.21.0->tensorboard<2.7,>=2.6.0->tensorflow) (1.26.8)\n",
      "Requirement already satisfied: certifi>=2017.4.17 in /opt/conda/lib/python3.7/site-packages (from requests<3,>=2.21.0->tensorboard<2.7,>=2.6.0->tensorflow) (2021.10.8)\n",
      "Requirement already satisfied: charset-normalizer~=2.0.0 in /opt/conda/lib/python3.7/site-packages (from requests<3,>=2.21.0->tensorboard<2.7,>=2.6.0->tensorflow) (2.0.12)\n",
      "Requirement already satisfied: idna<4,>=2.5 in /opt/conda/lib/python3.7/site-packages (from requests<3,>=2.21.0->tensorboard<2.7,>=2.6.0->tensorflow) (3.3)\n",
      "Requirement already satisfied: zipp>=0.5 in /opt/conda/lib/python3.7/site-packages (from importlib-metadata>=4.4->markdown>=2.6.8->tensorboard<2.7,>=2.6.0->tensorflow) (3.7.0)\n",
      "Requirement already satisfied: pyasn1<0.5.0,>=0.4.6 in /opt/conda/lib/python3.7/site-packages (from pyasn1-modules>=0.2.1->google-auth<2,>=1.6.3->tensorboard<2.7,>=2.6.0->tensorflow) (0.4.8)\n",
      "Requirement already satisfied: oauthlib>=3.0.0 in /opt/conda/lib/python3.7/site-packages (from requests-oauthlib>=0.7.0->google-auth-oauthlib<0.5,>=0.4.1->tensorboard<2.7,>=2.6.0->tensorflow) (3.2.0)\n"
     ]
    }
   ],
   "source": [
    "!pip3 install tensorflow"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Make sure to restart your kernel to ensure this change has taken place**."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 34
    },
    "colab_type": "code",
    "id": "IzbZLmz1dJmL",
    "outputId": "f4f882d9-6752-4b8d-8d7d-83eb61690d89"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2.6.3\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "import tensorflow as tf\n",
    "\n",
    "print(tf.__version__)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "36uCjFhldJmR"
   },
   "source": [
    "To start, we'll create our list of users, movies and features. While the users and movies represent elements in our database, for a content-based filtering method the features of the movies are likely hand-engineered and rely on domain knowledge to provide the best embedding space. Here we use the categories of Action, Sci-Fi, Comedy, Cartoon, and Drama to describe our movies (and thus our users).\n",
    "\n",
    "In this example, we will assume our database consists of four users and six movies, listed below.  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "ElQV43fxdJmS"
   },
   "outputs": [],
   "source": [
    "users = ['Ryan', 'Danielle',  'Vijay', 'Chris']\n",
    "movies = [\n",
    "    'Star Wars', 'The Dark Knight', 'Shrek',\n",
    "    'The Incredibles', 'Bleu', 'Memento'\n",
    "]\n",
    "features = ['Action', 'Sci-Fi', 'Comedy', 'Cartoon', 'Drama']\n",
    "\n",
    "num_users = len(users)\n",
    "num_movies = len(movies)\n",
    "num_feats = len(features)\n",
    "num_recommendations = 2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "s6iJCViqdJmU"
   },
   "source": [
    "### Initialize our users, movie ratings, and features\n",
    "\n",
    "We'll need to enter the user's movie ratings and the k-hot encoded movie features matrix. Each row of the users_movies matrix represents a single user's rating (from 1 to 10) for each movie. A zero indicates that the user has not seen/rated that movie. The movies_feats matrix contains the features for each of the given movies. Each row represents one of the six movies, while the columns represent the five categories. A one indicates that a movie fits within a given genre/category. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "_0asiLTwdJmV"
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2022-04-27 11:05:45.536583: I tensorflow/core/common_runtime/process_util.cc:146] Creating new thread pool with default inter op setting: 2. Tune using inter_op_parallelism_threads for best performance.\n"
     ]
    }
   ],
   "source": [
    "# Each row represents a user's rating for the different movies.\n",
    "users_movies = tf.constant([\n",
    "                [4,  6,  8,  0, 0, 0],\n",
    "                [0,  0, 10,  0, 8, 3],\n",
    "                [0,  6,  0,  0, 3, 7],\n",
    "                [10, 9,  0,  5, 0, 2]], dtype=tf.float32)\n",
    "\n",
    "# Features of the movies one-hot encoded.\n",
    "# e.g. columns could represent\n",
    "# ['Action', 'Sci-Fi', 'Comedy', 'Cartoon', 'Drama']\n",
    "movies_feats = tf.constant([\n",
    "                [1, 1, 0, 0, 1],\n",
    "                [1, 1, 0, 0, 0],\n",
    "                [0, 0, 1, 1, 0],\n",
    "                [1, 0, 1, 1, 0],\n",
    "                [0, 0, 0, 0, 1],\n",
    "                [1, 0, 0, 0, 1]], dtype=tf.float32)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "aCW5BtGudJmX"
   },
   "source": [
    "### Computing the user feature matrix\n",
    "\n",
    "We will compute the user feature matrix; that is, a matrix containing each user's embedding in the five-dimensional feature space.\n",
    "\n",
    "**TODO 1**: Calculuate this as the matrix multiplication of the `users_movies` tensor with the `movies_feats` tensor."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 101
    },
    "colab_type": "code",
    "id": "isMCBMOFdJmY",
    "outputId": "cf7eaa50-95ab-4e8f-916b-27c26d6421dd"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<tf.Tensor: shape=(4, 5), dtype=float32, numpy=\n",
       "array([[10., 10.,  8.,  8.,  4.],\n",
       "       [ 3.,  0., 10., 10., 11.],\n",
       "       [13.,  6.,  0.,  0., 10.],\n",
       "       [26., 19.,  5.,  5., 12.]], dtype=float32)>"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "users_feats = tf.matmul(users_movies, movies_feats)\n",
    "users_feats"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "Ps7XXoYwdJmc"
   },
   "source": [
    "Next we normalize each user feature vector to sum to 1. Normalizing isn't strictly neccesary, but it makes it so that rating magnitudes will be comparable between users."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 118
    },
    "colab_type": "code",
    "id": "y81EeooodJmc",
    "outputId": "904beb39-0a6f-49e0-971f-5198003e7adb"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<tf.Tensor: shape=(4, 5), dtype=float32, numpy=\n",
       "array([[0.25      , 0.25      , 0.2       , 0.2       , 0.1       ],\n",
       "       [0.0882353 , 0.        , 0.29411766, 0.29411766, 0.32352942],\n",
       "       [0.44827586, 0.20689656, 0.        , 0.        , 0.3448276 ],\n",
       "       [0.3880597 , 0.2835821 , 0.07462686, 0.07462686, 0.17910448]],\n",
       "      dtype=float32)>"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "users_feats = users_feats / tf.reduce_sum(users_feats, axis=1, keepdims=True)\n",
    "users_feats"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "kqOPr51tdJmf"
   },
   "source": [
    "#### Ranking feature relevance for each user\n",
    "\n",
    "We can use the users_feats computed above to represent the relative importance of each movie category for each user. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 101
    },
    "colab_type": "code",
    "id": "PKLqAD3adJmg",
    "outputId": "d535513e-72cd-4120-ef6d-82424efb20d4"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<tf.Tensor: shape=(4, 5), dtype=int32, numpy=\n",
       "array([[0, 1, 2, 3, 4],\n",
       "       [4, 2, 3, 0, 1],\n",
       "       [0, 4, 1, 2, 3],\n",
       "       [0, 1, 4, 2, 3]], dtype=int32)>"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "top_users_features = tf.nn.top_k(users_feats, num_feats)[1]\n",
    "top_users_features"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 84
    },
    "colab_type": "code",
    "id": "pvUmu7MUdJmj",
    "outputId": "a9e89bb0-330b-4687-866e-0f209910d8c0"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Ryan: ['Action', 'Sci-Fi', 'Comedy', 'Cartoon', 'Drama']\n",
      "Danielle: ['Drama', 'Comedy', 'Cartoon', 'Action', 'Sci-Fi']\n",
      "Vijay: ['Action', 'Drama', 'Sci-Fi', 'Comedy', 'Cartoon']\n",
      "Chris: ['Action', 'Sci-Fi', 'Drama', 'Comedy', 'Cartoon']\n"
     ]
    }
   ],
   "source": [
    "for i in range(num_users):\n",
    "    feature_names = [features[int(index)] for index in top_users_features[i]]\n",
    "    print('{}: {}'.format(users[i], feature_names))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "Yne0CyZMdJmn"
   },
   "source": [
    "### Determining movie recommendations. \n",
    "\n",
    "We'll now use the `users_feats` tensor we computed above to determine the movie ratings and recommendations for each user.\n",
    "\n",
    "To compute the projected ratings for each movie, we compute the similarity measure between the user's feature vector and the corresponding movie feature vector.  \n",
    "\n",
    "We will use the dot product as our similarity measure. In essence, this is a weighted movie average for each user.\n",
    "\n",
    "**TODO 2**: Implement this as a matrix multiplication. *Hint*: one of the operands will need to be transposed."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<tf.Tensor: shape=(4, 6), dtype=float32, numpy=\n",
       "array([[0.6       , 0.5       , 0.4       , 0.65      , 0.1       ,\n",
       "        0.35      ],\n",
       "       [0.4117647 , 0.0882353 , 0.5882353 , 0.67647064, 0.32352942,\n",
       "        0.4117647 ],\n",
       "       [1.        , 0.6551724 , 0.        , 0.44827586, 0.3448276 ,\n",
       "        0.79310346],\n",
       "       [0.8507463 , 0.6716418 , 0.14925373, 0.53731346, 0.17910448,\n",
       "        0.5671642 ]], dtype=float32)>"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "users_ratings = tf.matmul(users_feats, tf.transpose(movies_feats))\n",
    "users_ratings"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "o07wODzddJmq"
   },
   "source": [
    "The computation above finds the similarity measure between each user and each movie in our database. To focus only on the ratings for new movies, we apply a mask to the all_users_ratings matrix.  \n",
    "\n",
    "If a user has already rated a movie, we ignore that rating. This way, we only focus on ratings for previously unseen/unrated movies."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 168
    },
    "colab_type": "code",
    "id": "xUgOnV3AdJmr",
    "outputId": "2672899f-d626-4e33-e730-7d8b051a3954"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<tf.Tensor: shape=(4, 6), dtype=float32, numpy=\n",
       "array([[0.        , 0.        , 0.        , 0.65      , 0.1       ,\n",
       "        0.35      ],\n",
       "       [0.4117647 , 0.0882353 , 0.        , 0.67647064, 0.        ,\n",
       "        0.        ],\n",
       "       [1.        , 0.        , 0.        , 0.44827586, 0.        ,\n",
       "        0.        ],\n",
       "       [0.        , 0.        , 0.14925373, 0.        , 0.17910448,\n",
       "        0.        ]], dtype=float32)>"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "users_unseen_movies = tf.equal(users_movies, tf.zeros_like(users_movies))\n",
    "ignore_matrix = tf.zeros_like(tf.cast(users_movies, tf.float32))\n",
    "\n",
    "users_ratings_new = tf.where(\n",
    "    users_unseen_movies,\n",
    "    users_ratings,\n",
    "    ignore_matrix)\n",
    "\n",
    "users_ratings_new"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "YyNvH46zdJmu"
   },
   "source": [
    "Finally, let's grab and print out the top 2 rated movies for each user."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 101
    },
    "colab_type": "code",
    "id": "PdDGgmSpdJmv",
    "outputId": "a921b943-383b-4984-cffd-e0eb5c7ab41e"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<tf.Tensor: shape=(4, 2), dtype=int32, numpy=\n",
       "array([[3, 5],\n",
       "       [3, 0],\n",
       "       [0, 3],\n",
       "       [4, 2]], dtype=int32)>"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "top_movies = tf.nn.top_k(users_ratings_new, num_recommendations)[1]\n",
    "top_movies"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 84
    },
    "colab_type": "code",
    "id": "dCB7Dv9_dJmx",
    "outputId": "0d00e5c6-f7bc-4fae-a359-283f2fdb1c4c"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Ryan: ['The Incredibles', 'Memento']\n",
      "Danielle: ['The Incredibles', 'Star Wars']\n",
      "Vijay: ['Star Wars', 'The Incredibles']\n",
      "Chris: ['Bleu', 'Shrek']\n"
     ]
    }
   ],
   "source": [
    "for i in range(num_users):\n",
    "    movie_names = [movies[index] for index in top_movies[i]]\n",
    "    print('{}: {}'.format(users[i], movie_names))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Copyright 2022 Google Inc.\n",
    "Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except in compliance with the License. You may obtain a copy of the License at\n",
    "http://www.apache.org/licenses/LICENSE-2.0\n",
    "Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License."
   ]
  }
 ],
 "metadata": {
  "colab": {
   "name": "content_based_by_hand.ipynb",
   "provenance": [],
   "version": "0.3.2"
  },
  "environment": {
   "kernel": "python3",
   "name": "tf2-gpu.2-6.m91",
   "type": "gcloud",
   "uri": "gcr.io/deeplearning-platform-release/tf2-gpu.2-6:m91"
  },
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
